
package com.example.websocketserver.configuration;

import com.example.websocketserver.configuration.client.MsgCenterReactorNettyTcpClient;
import com.example.websocketserver.configuration.handler.WebsocketShakeHandler;
import com.example.websocketserver.configuration.interceptors.HandleShakeInterceptors;
import com.example.websocketserver.configuration.interceptors.StompUserInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.ByteArrayMessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompReactorNettyCodec;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.*;

import java.util.List;

/**
 * 注解开启STOMP协议来传输基于代理的消息，此时控制器支持使用@MessageMapping
 *
 * @author liulei
 * @version 1.0
 */
@Configuration
@RequiredArgsConstructor
public class WebSocketAutoConfig extends WebSocketMessageBrokerConfigurationSupport {
    private final WebsocketShakeHandler shakeHandler;
    private final HandleShakeInterceptors shakeInterceptors;
    private final StompUserInterceptor stompUserInterceptor;
    private final WebsocketMqProxyConfig mqProxyConfig;

    /**
     * 注册 Stomp的端点
     * addEndpoint：添加STOMP协议的端点。这个HTTP URL是供WebSocket或SockJS客户端访问的地址
     * withSockJS：指定端点使用SockJS协议
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setHandshakeHandler(shakeHandler)
                .addInterceptors(shakeInterceptors)
                .setAllowedOrigins("*")
                /*.withSockJS()*/;//如果使用在线websocket测试，此处需要注释，如果使用test.html的测试代码则需要放开
    }

    /**
     * 配置消息代理
     * 使用RabbitMQ的消息中继代理，作为消息发送、接收中转站，替换默认的内存级别的Simple Broker
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // TODO setTcpClient(tcpClient());使用 setTcpClient 以配置多个 broker 地址，setRelayHost/Port 只能配置一个
        registry.enableStompBrokerRelay("/topic", "/queue")

                // 单个stomp代理服务器
                .setRelayHost(mqProxyConfig.getStompAddress())
                .setRelayPort(mqProxyConfig.getStompProxyPort())
                // 使用 setTcpClient 以配置多个 broker 地址，setRelayHost/Port 只能配置一个
                // .setTcpClient(tcpClient())

                .setSystemLogin(mqProxyConfig.getSystemLogin())
                .setSystemPasscode(mqProxyConfig.getSystemPasscode())
                .setClientLogin(mqProxyConfig.getClientLogin())
                .setClientPasscode(mqProxyConfig.getClientPasscode())
                .setVirtualHost(mqProxyConfig.getVHost())
                .setSystemHeartbeatSendInterval(mqProxyConfig.getSenderHeartbeat())
                .setSystemHeartbeatReceiveInterval(mqProxyConfig.getReceiverHeartbeat());
        // 默认前缀[都是user]
        registry.setUserDestinationPrefix("/user");
    }

    /**
     * Tcp client msg center reactor netty tcp client.
     *
     * @return the msg center reactor netty tcp client
     */
    public MsgCenterReactorNettyTcpClient tcpClient() {
        WebsocketMqProxyConfig.BrokerAddressSupplier brokerAddressSupplier = mqProxyConfig.getBrokerAddressSupplier();
        return new MsgCenterReactorNettyTcpClient(tcpClient -> tcpClient.addressSupplier(brokerAddressSupplier), new StompReactorNettyCodec());
    }

    @Override
    public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
        messageConverters.add(new ByteArrayMessageConverter());
        return true;
    }

    /**
     * 消息传输参数配置（可选）
     */
    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
        //设置消息字节数大小
        registry.setMessageSizeLimit(8192)
                //设置消息缓存大小
                .setSendBufferSizeLimit(8192)
                //设置消息发送时间限制毫秒
                .setSendTimeLimit(10000);
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(stompUserInterceptor).taskExecutor()
                .corePoolSize(4)
                .maxPoolSize(16)
                .keepAliveSeconds(600);
    }

    @Bean
    @Override
    public WebSocketHandler subProtocolWebSocketHandler() {
        return new MySubProtocolWebSocketHandler(clientInboundChannel(), clientOutboundChannel());
    }
}
